package com.apes.framework.plugin.esb.api;

import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.lock.IsTryAgain;
import com.apes.framework.jpa.logger.ApesLoggerService;
import com.apes.framework.plugin.esb.processor.Processor;
import com.apes.framework.plugin.event.Listener;
import com.apes.framework.plugin.filereader.xml.ApesElement;
import com.apes.framework.plugin.log.timer.Timer;
import com.apes.framework.rop.session.SimpleSession;
import com.apes.framework.rop.session.index.SessionIndexItem;
import com.apes.framework.rop.session.index.SessionIndexManager;
import com.apes.framework.util.ApesConst;
import com.apes.framework.util.EventUtil;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.statistics.Statistics;
import com.apes.framework.util.statistics.StatisticsService;
import com.apes.oap.response.OapResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.spi.PersistenceUnitInfo;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能：Esb路由入口
 *
 * @author xul
 * @create 2017-12-04 16:56
 */
@Component("esbRouter")
public class EsbRouter {
    private static final Map<String, ApesElement> routers = new HashMap();

    public Processor getProcessor(String method, String version) {
        ApesElement apesElement = getApesElement(method, version);
        if (apesElement == null) throw new RuntimeException("服务：" + method + " 在系统中没有配置！");
        Processor processor = apesElement.parse();
        return processor;
    }

    public ApesElement getApesElement(String method, String version) {
        return routers.get(methodWithVersion(method, version));
    }

    public static String methodWithVersion(String method, String version) {
        return method + "#" + version;
    }

    private static void setRouter(String key, ApesElement apesElement) {
        routers.put(key, apesElement);
    }

    public static void setRouter(String method, String version, ApesElement apesElement) {
        setRouter(methodWithVersion(method, version), apesElement);
        SpringManager.getBean(ServiceMethodManager.class).addServiceMethod(method, version);
    }

    public static void removeRouter(String method, String version) {
        routers.remove(methodWithVersion(method, version));
        SpringManager.getBean(ServiceMethodManager.class).removeServiceMethod(method, version);
    }

    public static Map<String, ApesElement> getRouters() {
        return routers;
    }

    public static ApesElement getRouter(String method) {
        return getRouter(method, ApesConst.METHOD_VERSION);
    }

    public static ApesElement getRouter(String method, String version) {
        return routers.get(methodWithVersion(method, version));
    }

    public Page<Map> getAllProcessor(SimpleRequest request) {
        List<Map> processors = new ArrayList<>();
        routers.forEach((k, v) -> {
            Map m = new HashMap();
            m.put("id", v.getAttribute("method"));
            m.put("name", v.getAttribute("description"));
            processors.add(m);
        });

        List<Map> collect = processors.stream().filter(m ->
                ((String) m.get("id")).contains(request.get("key")) || (((String) m.get("name")) != null && ((String) m.get("name")).contains(request.get("key")))
        ).collect(Collectors.toList());

        Pageable pageable = PageRequest.of(request.get("page"), request.get("size"));
        PageImpl page = new PageImpl<>(collect, pageable, collect.size());
        return page;
    }

    public Page<Map> getAllDomainType(SimpleRequest request) {
        EntityManagerFactoryInfo emfi = SpringManager.getBean(EntityManagerFactoryInfo.class);
        PersistenceUnitInfo pui = emfi.getPersistenceUnitInfo();
        Pageable pageable = PageRequest.of(request.get("page"), request.get("size"));
        List<String> classNames = pui.getManagedClassNames().stream().filter(name -> name.contains(request.get("key"))).collect(Collectors.toList());

        PageImpl page = new PageImpl<>(classNames, pageable, classNames.size());
        return page;
    }

    public <T> T innerCall(SimpleRequest request) {
        String method = request.getRopRequestContext().getMethod();
        String version = request.getRopRequestContext().getVersion();
        /**测试包装服务接口**/
        if (method.equals("test.wrapper")) {
            method = request.get("method");
        }
        Processor<SimpleRequest, Object> processor = getProcessor(method, version);
        T result = (T) processor.process(request);
        SimpleSession session = request.getSession();
        logger(result, getApesElement(method, version), session != null ? session.getUserId() : null, session != null ? session.getAttribute("workPostName", String.class) : null);
        if (session != null) {
            Integer userId = Integer.valueOf(session.getUserId());
            SessionIndexManager sessionIndexManager = SpringManager.getBean(SessionIndexManager.class);
            List<SessionIndexItem> sessionIndexItems = (List<SessionIndexItem>) sessionIndexManager.get(userId);
            if (sessionIndexItems != null) {
                sessionIndexItems.stream().filter(item -> item.getSessionId().equals(session.getSessionId())).forEach(item -> item.setUpdated(new Date().getTime()));
                sessionIndexManager.add(userId, sessionIndexItems);
            }
        }
        return result;
    }

    public <T> T call(SimpleRequest request) {
        Object result = innerCall(request);
        return (T) MapUtil.mapped(result);
    }

    private void logger(Object entity, ApesElement element, String userId, String workPostName) {
        if (entity == null) return;
        if (!(entity instanceof Model)) return;
        Model model = (Model) entity;
        if (!model.isWrite()) return;
        SpringManager.getBean(ApesLoggerService.class).sendSynEvent("event:ir.logger.create", MapUtil.mapper(
                "businessType", model.getInnerBusinessType(),
                "businessKey", model.getInnerBusinessKey(),
                "operate", element.getAttribute("description"),
                "workPostName", workPostName,
                "audit", model.getAudit(),
                "operator_id", StringUtils.hasText(model.getUserId()) ? model.getUserId() : userId,
                "operatorName", StringUtils.hasText(model.getUserName()) ? model.getUserName() : ""));
    }

    /**
     * 外部调用，没有注入事务
     **/
    public OapResponse process(SimpleRequest request) {
        Object result = call(request);
        return new OapResponse().setBody(result);
    }

    /**
     * 外部调用，声明式事务
     */
    @IsTryAgain
    @Timer("ESB服务调用")
    @Transactional(rollbackFor = Exception.class)
    public OapResponse invoke(SimpleRequest request) {
        return process(request);
    }


    @Autowired
    private StatisticsService statisticsService;

    /**
     * ESB服务方法内部调用
     */
    @Listener(topic = "event:esbRouter.innerCall")
    public <T> T eventInvoke(SimpleRequest request) {
        SimpleRequest simpleRequest = (SimpleRequest) request.getData();
        Statistics statistics = statisticsService.createStatistics(simpleRequest);
        try {
            Object result = innerCall(simpleRequest);
            if (statistics != null) {
                statistics.setResponse(statisticsService.toJSONString(result));
                EventUtil.sendAynEvent(this, "event:log.message.save", statistics);
            }
            return (T) result;
        } catch (Exception e) {
            if (statistics != null) {
                statistics.setResponse(e.getMessage());
                EventUtil.sendAynEvent(this, "event:log.message.save", statistics);
            }
            throw e;
        }
    }

}
